package com.dimples.dd.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.shaded.com.google.common.annotations.VisibleForTesting;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dimples.dd.common.constant.DDConstant;
import com.dimples.dd.common.enums.CommonStatusEnum;
import com.dimples.dd.common.model.DDAuthUser;
import com.dimples.dd.common.model.Option;
import com.dimples.dd.common.pojo.PageResult;
import com.dimples.dd.common.util.collection.CollectionUtils;
import com.dimples.dd.security.core.service.PermUtilService;
import com.dimples.dd.security.core.util.SecurityUtils;
import com.dimples.dd.system.convert.UserConvert;
import com.dimples.dd.system.mapper.SystemUserPostMapper;
import com.dimples.dd.system.mapper.SystemUserRoleMapper;
import com.dimples.dd.system.mapper.SystemUsersMapper;
import com.dimples.dd.system.model.bo.SimpleUserBO;
import com.dimples.dd.system.model.bo.UserBO;
import com.dimples.dd.system.model.bo.UserFormBO;
import com.dimples.dd.system.model.entity.*;
import com.dimples.dd.system.model.form.UserForm;
import com.dimples.dd.system.model.query.UserPageQuery;
import com.dimples.dd.system.model.vo.UserInfoVO;
import com.dimples.dd.system.model.vo.UserPageVO;
import com.dimples.dd.system.model.vo.UserSimpleVO;
import com.dimples.dd.system.service.*;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.dimples.dd.common.exception.BizExceptionUtil.exception;
import static com.dimples.dd.common.exception.ErrorCode.*;
import static com.dimples.dd.system.enums.LogRecordConstants.*;

/**
 * 用户业务实现类
 *
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2024/6/11
 */
@RequiredArgsConstructor
@Service
public class SystemUsersServiceImpl extends ServiceImpl<SystemUsersMapper, SystemUsersDO> implements SystemUsersService {

    private final Set<String> onlineUsers = ConcurrentHashMap.newKeySet();

    private final UserConvert userConvert;
    private final PasswordEncoder passwordEncoder;
    private final SystemUserRoleMapper systemUserRoleMapper;
    private final SystemMenuService systemMenuService;
    private final SystemRoleService systemRoleService;
    private final SystemDeptService systemDeptService;
    private final PermUtilService permUtilService;
    private final SystemUserPostMapper systemUserPostMapper;

    @Autowired
    private PermissionService permissionService;

    /**
     * 根据用户名获取认证信息
     *
     * @param username 用户名
     * @return {@link com.dimples.dd.common.model.DDAuthUser}
     */
    @Override
    public DDAuthUser getUserAuthUser(String username) {
        SystemUsersDO systemUsersDO = this.baseMapper.findByName(username);
        DDAuthUser authUser = userConvert.toAuthUser(systemUsersDO);
        if (systemUsersDO != null) {
            List<SystemMenuDO> userPermissions = this.systemMenuService.findUserPermissions(username);
            if (CollUtil.isNotEmpty(userPermissions)) {
                authUser.setPerms(userPermissions.stream().map(SystemMenuDO::getPermission).collect(Collectors.toSet()));
            }
        }

        return authUser;
    }

    @Override
    public Page<UserPageVO> getUserPage(UserPageQuery queryParams) {
        // 如果部门id不为空，查询该部门下（包括子部门）所有员工信息
        if (ObjUtil.isNotEmpty(queryParams.getDeptId())) {
            List<Long> deptChildrenIds = this.systemDeptService.selectDeptChildrenIds(queryParams.getDeptId());
            queryParams.setDeptIds(deptChildrenIds);
        }
        // 参数构建;
        Page<UserBO> page = new Page<>(queryParams.getCurrent(), queryParams.getSize());

        // 查询数据
        Page<UserBO> userBoPage = this.baseMapper.getUserPage(page, queryParams);

        // 实体转换
        return userConvert.bo2Vo(userBoPage);
    }

    @Override
    public UserForm getUserFormData(Long userId) {
        UserFormBO userFormBO = this.baseMapper.getUserDetail(userId);
        // 实体转换po->form
        return userConvert.bo2Form(userFormBO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}",
            success = SYSTEM_USER_CREATE_SUCCESS)
    public void createUser(UserForm userForm) {
        // 1.1 校验租户账户配额

        // 1.2 校验正确性
        validateUserForCreateOrUpdate(null, userForm.getUsername(),
                userForm.getMobile(), userForm.getEmail(), userForm.getDeptId(), userForm.getPostIds());
        // 2.1 插入用户
        SystemUsersDO user = userConvert.form2Entity(userForm);
        // 默认开启
        user.setStatus(CommonStatusEnum.ENABLE.getValue().byteValue());
        // 设置默认加密密码
        user.setPassword(passwordEncoder.encode(DDConstant.DEFAULT_PASSWORD));

        // 新增用户
        this.save(user);

        // 2.2 保存用户角色
        List<Long> roleIds = systemRoleService.listIdsByCodes(userForm.getRoles());
        this.permissionService.saveUserRoles(user.getId(), roleIds);
        // 2.3 插入关联岗位
        if (CollectionUtil.isNotEmpty(user.getPostIds())) {
            this.systemUserPostMapper.insertBatch(CollectionUtils.convertList(user.getPostIds(), postId -> new SystemUserPostDO().setUserId(user.getId()).setPostId(postId)));
        }

        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("user", user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_SUB_TYPE, bizNo = "{{#userForm.id}}",
            success = SYSTEM_USER_UPDATE_SUCCESS)
    public void updateUser(UserForm userForm) {
        // 1. 校验正确性
        SystemUsersDO oldUser = validateUserForCreateOrUpdate(userForm.getId(), userForm.getUsername(),
                userForm.getMobile(), userForm.getEmail(), userForm.getDeptId(), userForm.getPostIds());

        // form -> entity
        SystemUsersDO updateObj = userConvert.form2Entity(userForm);

        // 2.1 修改用户
        this.updateById(updateObj);
        // 2.2 保存用户角色
        List<Long> roleIds = systemRoleService.listIdsByCodes(userForm.getRoles());
        this.permissionService.saveUserRoles(updateObj.getId(), roleIds);
        // 2.3 更新岗位
        this.updateUserPost(userForm);

        // 3. 记录操作日志上下文
        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtil.toBean(oldUser, UserForm.class));
        LogRecordContext.putVariable("user", oldUser);
    }

    private void updateUserPost(UserForm userForm) {
        Long userId = userForm.getId();
        Set<Long> dbPostIds = CollectionUtils.convertSet(this.systemUserPostMapper.selectListByUserId(userId), SystemUserPostDO::getPostId);
        // 计算新增和删除的岗位编号
        Set<Long> postIds = userForm.getPostIds();
        Collection<Long> createPostIds = CollUtil.subtract(postIds, dbPostIds);
        Collection<Long> deletePostIds = CollUtil.subtract(dbPostIds, postIds);
        // 执行新增和删除。对于已经授权的岗位，不用做任何处理
        if (!CollectionUtil.isEmpty(createPostIds)) {
            this.systemUserPostMapper.insertBatch(CollectionUtils.convertList(createPostIds,
                    postId -> new SystemUserPostDO().setUserId(userId).setPostId(postId)));
        }
        if (!CollectionUtil.isEmpty(deletePostIds)) {
            this.systemUserPostMapper.deleteByUserIdAndPostId(userId, deletePostIds);
        }
    }

    @Override
    public Boolean updateUserStatus(Long userId) {
        // 校验用户存在
        SystemUsersDO usersDO = validateUserExists(userId);
        // 更新状态
        SystemUsersDO updateObj = new SystemUsersDO();
        updateObj.setId(userId);
        updateObj.setStatus(usersDO.getStatus().intValue() == 1 ? ByteUtil.intToByte(0) : ByteUtil.intToByte(1));
        return this.baseMapper.updateById(updateObj) > 0;
    }

    @Override
    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#userId}}",
            success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS)
    public void updateUserPassword(Long userId, String password) {
        // 1. 校验用户存在
        SystemUsersDO user = validateUserExists(userId);

        // 2. 更新密码
        SystemUsersDO updateObj = new SystemUsersDO();
        updateObj.setId(userId);
        updateObj.setPassword(this.passwordEncoder.encode(password)); // 加密密码
        this.baseMapper.updateById(updateObj);

        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("user", user);
        LogRecordContext.putVariable("newPassword", updateObj.getPassword());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#userIds}}",
            success = SYSTEM_USER_DELETE_SUCCESS)
    public void deleteUsers(String[] userIds) {
        // 1. 校验用户存在
        List<SystemUsersDO> users = this.baseMapper.selectBatchIds(Arrays.asList(userIds));
        if (CollUtil.isEmpty(users)) {
            throw exception(USER_NOT_EXISTS);
        }
        // 2.1 删除用户
        this.removeByIds(CollUtil.newArrayList(userIds));
        // 2.2 删除用户关联数据
        this.systemUserRoleMapper.delete(Wrappers.lambdaUpdate(SystemUserRoleDO.class).in(SystemUserRoleDO::getUserId, CollUtil.newArrayList(userIds)));
        // 2.3 删除用户岗位
        this.systemUserPostMapper.deleteByUserIds(CollectionUtils.convertSet(Arrays.asList(userIds)));

        // 3. 记录操作日志上下文
        SystemUsersDO usersDO = new SystemUsersDO();
        usersDO.setNickname(StrUtil.join(StrUtil.COMMA, users.stream().map(SystemUsersDO::getNickname).collect(Collectors.toList())));
        LogRecordContext.putVariable("user", usersDO);
    }

    @Override
    public UserInfoVO getCurrentUserInfo() {
        // 登录用户entity
        SystemUsersDO user = this.getOne(new LambdaQueryWrapper<SystemUsersDO>()
                .eq(SystemUsersDO::getUsername, SecurityUtils.getUsername())
                .select(
                        SystemUsersDO::getId,
                        SystemUsersDO::getUsername,
                        SystemUsersDO::getNickname,
                        SystemUsersDO::getRemark,
                        SystemUsersDO::getAvatar
                )
        );
        // entity->VO
        UserInfoVO userInfoVO = userConvert.entity2UserInfoVo(user);

        // 获取用户角色集合
        Set<String> roles = SecurityUtils.getRoles();
        userInfoVO.setRoles(roles);

        // 获取用户权限集合
        // todo: 暂时获取用户的所有角色下的所有权限，是否考虑用户存在多个角色时，根据前端切换角色来获取不同角色的不同权限
        if (CollectionUtil.isNotEmpty(roles)) {
            Set<String> perms = permUtilService.getRolePermsFormCache(roles);
            userInfoVO.setPerms(perms);
        }

        return userInfoVO;
    }

    @Override
    public List<SystemUsersDO> getUserListByStatus(Integer status) {
        return this.baseMapper.selectListByStatus(status);
    }

    @Override
    public List<UserSimpleVO> getSimpleUserList() {
        List<SystemUsersDO> list = this.getUserListByStatus(CommonStatusEnum.ENABLE.getValue());
        // 拼接数据
        Map<Long, SystemDeptDO> deptMap = this.systemDeptService.selectDeptMap(CollectionUtils.convertList(list, SystemUsersDO::getDeptId));
        return this.userConvert.convertSimpleList(list, deptMap);
    }

    @Override
    public PageResult<Option<Long>> listUserOptions(UserPageQuery userPageQuery) {
        PageResult<SimpleUserBO> pageResult = this.baseMapper.listUserOptions(userPageQuery);
        return this.userConvert.bo2OptionPage(pageResult);
    }

    private SystemUsersDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
                                                        Long deptId, Set<Long> postIds) {
        // 校验用户存在
        SystemUsersDO user = validateUserExists(id);
        // 校验用户名唯一
        validateUsernameUnique(id, username);
        // 校验手机号唯一
        validateMobileUnique(id, mobile);
        // 校验邮箱唯一
        validateEmailUnique(id, email);
        // 校验部门处于开启状态
        systemDeptService.validateDeptList(CollectionUtils.singleton(deptId));
        // 校验岗位处于开启状态
//        postService.validatePostList(postIds);
        return user;
    }

    @VisibleForTesting
    SystemUsersDO validateUserExists(Long id) {
        if (id == null) {
            return null;
        }
        SystemUsersDO user = this.baseMapper.selectById(id);
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        return user;
    }

    @VisibleForTesting
    void validateUsernameUnique(Long id, String username) {
        if (StrUtil.isBlank(username)) {
            return;
        }
        SystemUsersDO user = this.baseMapper.selectOne(Wrappers.lambdaQuery(SystemUsersDO.class).eq(SystemUsersDO::getUsername, username));
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw exception(USER_USERNAME_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw exception(USER_USERNAME_EXISTS);
        }
    }

    @VisibleForTesting
    void validateEmailUnique(Long id, String email) {
        if (StrUtil.isBlank(email)) {
            return;
        }
        SystemUsersDO user = this.baseMapper.selectOne(Wrappers.lambdaQuery(SystemUsersDO.class).eq(SystemUsersDO::getEmail, email));
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw exception(USER_EMAIL_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw exception(USER_EMAIL_EXISTS);
        }
    }

    @VisibleForTesting
    void validateMobileUnique(Long id, String mobile) {
        if (StrUtil.isBlank(mobile)) {
            return;
        }
        SystemUsersDO user = this.baseMapper.selectOne(Wrappers.lambdaQuery(SystemUsersDO.class).eq(SystemUsersDO::getMobile, mobile));
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw exception(USER_MOBILE_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw exception(USER_MOBILE_EXISTS);
        }
    }

    @Override
    public void updateUserLogin(Long userId, String loginIp) {
        this.baseMapper.updateById(new SystemUsersDO().setId(userId).setLoginIp(loginIp).setLoginDate(LocalDateTime.now()));
    }

    @Override
    public void addOnlineUser(String username) {
        onlineUsers.add(username);
    }

    @Override
    public void removeOnlineUser(String username) {
        onlineUsers.remove(username);
    }

    @Override
    public Set<String> getAllOnlineUsers() {
        return Collections.unmodifiableSet(onlineUsers);
    }

    @Override
    public Set<String> getOnlineReceivers(Set<String> receivers) {
        return receivers.stream().filter(onlineUsers::contains).collect(Collectors.toSet());
    }

    @Override
    public int getOnlineUserCount() {
        return onlineUsers.size();
    }
}
